package cn.ycc1.baseset.generics;

/**
 * Restriction on Generics
 * @author ycc
 * @date 2025/3/5
 */
public class RestrictionOnGenerics {
    /**
     * Cannot Instantiate Generic Types with Primitive Types
     * Consider the following parameterized type:
     *
     */
    class Pair<K, V> {

        private K key;
        private V value;

        public Pair(K key, V value) {
            this.key = key;
            this.value = value;
        }

        // ...
    }
    /**
     * When creating a Pair object, you cannot substitute a primitive type for the type parameter K or V:
     *
     * Pair<int, char> p = new Pair<>(8, 'a');  // compile-time error
     * You can substitute only non-primitive types for the type parameters K and V:
     *
     * Pair<Integer, Character> p = new Pair<>(8, 'a');
     * Note that the Java compiler autoboxes 8 to Integer.valueOf(8) and 'a' to Character('a'):
     *
     * Pair<Integer, Character> p = new Pair<>(Integer.valueOf(8), new Character('a'));
     * For more information on autoboxing, see Autoboxing and Unboxing in the Numbers and Strings section.
     */

    /**
     * Cannot Create Instances of Type Parameters
     * You cannot create an instance of a type parameter. For example, the following code causes a compile-time error:
     *
     * public static <E> void append(List<E> list) {
     *     E elem = new E();  // compile-time error
     *     list.add(elem);
     * }
     * As a workaround, you can create an object of a type parameter through reflection:
     *
     * public static <E> void append(List<E> list, Class<E> cls) throws Exception {
     *     E elem = cls.newInstance();   // OK
     *     list.add(elem);
     * }
     * You can invoke the append() method as follows:
     *
     * List<String> ls = new ArrayList<>();
     * append(ls, String.class);
     */

    /**
     * Cannot Declare Static Fields Whose Types are Type Parameters
     * A class's static field is a class-level variable shared by all non-static objects of the class. Hence, static
     * fields of type parameters are not allowed. Consider the following class:
     *
     * public class MobileDevice<T> {
     *     private static T os;
     *
     *     // ...
     * }
     * If static fields of type parameters were allowed, then the following code would be confused:
     *
     * MobileDevice<Smartphone> phone = new MobileDevice<>();
     * MobileDevice<Pager> pager = new MobileDevice<>();
     * MobileDevice<TabletPC> pc = new MobileDevice<>();
     * Because the static field os is shared by phone, pager, and pc, what is the actual type of os? It cannot be Smartphone,
     * Pager, and TabletPC at the same time. You cannot, therefore, create static fields of type parameters.
     */

    /**
     * Cannot Use Casts or instanceof with Parameterized Types
     * 不能将强制转换或实例化与参数化类型一起使用
     * Because the Java compiler erases all type parameters in generic code, you cannot verify which parameterized type for
     * a generic type is being used at runtime:
     *
     * public static <E> void rtti(List<E> list) {
     *     if (list instanceof ArrayList<Integer>) {  // compile-time error
     *         // ...
     *     }
     * }
     * The set of parameterized types passed to the rtti() method is:
     *
     * S = { ArrayList<Integer>, ArrayList<String> LinkedList<Character>, ... }
     * The runtime does not keep track of type parameters, so it cannot tell the difference between an ArrayList<Integer>
     *     and an ArrayList<String>. The most you can do is to use an unbounded wildcard to verify that the list is an ArrayList:
     *
     * public static void rtti(List<?> list) {
     *     if (list instanceof ArrayList<?>) {  // OK; instanceof requires a reifiable type
     *         // ...
     *     }
     * }
     * Typically, you cannot cast to a parameterized type unless it is parameterized by unbounded wildcards. For example:
     *
     * List<Integer> li = new ArrayList<>();
     * List<Number>  ln = (List<Number>) li;  // compile-time error
     * However, in some cases the compiler knows that a type parameter is always valid and allows the cast. For example:
     *
     * List<String> l1 = ...;
     * ArrayList<String> l2 = (ArrayList<String>)l1;  // OK
     */

    /**
     * Cannot Create Arrays of Parameterized Types 无法创建参数化类型的数组
     * You cannot create arrays of parameterized types. For example, the following code does not compile:
     *
     * List<Integer>[] arrayOfLists = new List<Integer>[2];  // compile-time error
     * The following code illustrates what happens when different types are inserted into an array:
     *
     * Object[] strings = new String[2];
     * strings[0] = "hi";   // OK
     * strings[1] = 100;    // An ArrayStoreException is thrown.
     * If you try the same thing with a generic list, there would be a problem:
     *
     * Object[] stringLists = new List<String>[2];  // compiler error, but pretend it's allowed
     * stringLists[0] = new ArrayList<String>();   // OK
     * stringLists[1] = new ArrayList<Integer>();  // An ArrayStoreException should be thrown,
     *                                             // but the runtime can't detect it.
     * If arrays of parameterized lists were allowed, the previous code would fail to throw the desired ArrayStoreException.
     */

//    Cannot Create, Catch, or Throw Objects of Parameterized Types
//    A generic class cannot extend the Throwable class directly or indirectly. For example, the following classes will not compile:
//
//    // Extends Throwable indirectly
//    class MathException<T> extends Exception { /* ... */ }    // compile-time error
//
//    // Extends Throwable directly
//    class QueueFullException<T> extends Throwable { /* ... */ // compile-time error
//        A method cannot catch an instance of a type parameter:
//
//        public static <T extends Exception, J> void execute(List<J> jobs) {
//            try {
//                for (J job : jobs)
//                // ...
//            } catch (T e) {   // compile-time error
//                // ...
//            }
//        }
//        You can, however, use a type parameter in a throws clause:
//
//        class Parser<T extends Exception> {
//            public void parse(File file) throws T {     // OK
//                // ...
//            }
//        }
    /**
     * Cannot Overload a Method Where the Formal Parameter Types of Each Overload Erase to the Same Raw Type
     * A class cannot have two overloaded methods that will have the same signature after type erasure.
     *
     * public class Example {
     *     public void print(Set<String> strSet) { }
     *     public void print(Set<Integer> intSet) { }
     * }
     * The overloads would all share the same classfile representation and will generate a compile-time error.
     */

}
